#include "MainWindow.hpp"
#include "DialogNewMap.hpp"
#include "DialogNewTexture.hpp"
#include "3D/DrawableObject.hpp"
#include "3D/TerrainFactory.hpp"
#include "3D/Texture2D.hpp"
#include "3D/ObjModel.hpp"
#include "Scene.hpp"
#include "ui_MainWindow.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QGLFormat>

MainWindow::MainWindow(QWidget *parent):
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    _glDisplay(NULL),
    _currentBrush(BRUSH_LITTLE),
    _currentCamera(CAMERA_FIXED)

{
    ui->setupUi(this);

    initMenus();
    initWidgets();
}

MainWindow::~MainWindow()
{
    delete _glDisplay;
    delete ui;
}


void    MainWindow::initMenus()
{
    QObject::connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(menuActionAbout()));

    QObject::connect(ui->actionLoadMap, SIGNAL(triggered()), this, SLOT(menuActionLoadMap()));
    QObject::connect(ui->actionSaveMap, SIGNAL(triggered()), this, SLOT(menuActionSaveMap()));
    QObject::connect(ui->actionSaveMapAs, SIGNAL(triggered()), this, SLOT(menuActionSaveMapAs()));
    QObject::connect(ui->actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));

    QObject::connect(ui->actionNewMap, SIGNAL(triggered()), this, SLOT(menuActionNewMap()));
    QObject::connect(ui->actionImportTexture, SIGNAL(triggered()), this, SLOT(menuActionImportTexture()));
    QObject::connect(ui->actionImportModel, SIGNAL(triggered()), this, SLOT(menuActionImportModel()));

    QObject::connect(ui->actionFill, SIGNAL(triggered()), this, SLOT(menuActionFill()));
    QObject::connect(ui->actionLine, SIGNAL(triggered()), this, SLOT(menuActionLine()));
    QObject::connect(ui->actionPoint, SIGNAL(triggered()), this, SLOT(menuActionPoint()));

    QObject::connect(ui->listWidgetTexture, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(handleTextureListClick(QListWidgetItem*)));
    QObject::connect(ui->listWidgetModel, SIGNAL(itemClicked(QListWidgetItem *)), this, SLOT(handleModelListClick(QListWidgetItem*)));

    ui->actionNewMap->setShortcut(QKeySequence("Ctrl+N"));
    ui->actionImportModel->setShortcut(QKeySequence("Ctrl+M"));
    ui->actionImportTexture->setShortcut(QKeySequence("Ctrl+T"));

    ui->actionLoadMap->setShortcut(QKeySequence("Ctrl+L"));
    ui->actionSaveMap->setShortcut(QKeySequence("Ctrl+S"));
    ui->actionSaveMapAs->setShortcut(QKeySequence("Ctrl+Shift+S"));
    ui->actionQuit->setShortcut(QKeySequence("Ctrl+Q"));
    ui->actionAbout->setShortcut(QKeySequence("F1"));

    ui->actionFill->setShortcut(QKeySequence("1"));
    ui->actionLine->setShortcut(QKeySequence("2"));
    ui->actionPoint->setShortcut(QKeySequence("3"));
}

void    MainWindow::initWidgets()
{
    this->centralWidget()->setLayout(ui->mainLayout);
    ui->listWidgetTexture->addItem(new QListWidgetItem("None"));
    init3DMenuOptions();

    try
    {
        QGLFormat   format;

        format.setVersion(3, 1);
        format.setDoubleBuffer(true);
        format.setDepth(true);
        format.setDepthBufferSize(24);
        format.setSampleBuffers(true);
        format.setSwapInterval(1);

        _glDisplay = new OglWidget(format, this);
        _glDisplay->setCameraType(_currentCamera);
        _glDisplay->setBrushType(_currentBrush);

        ui->drawLayout->addWidget(_glDisplay);
    }
    catch (std::exception e)
    {
        QMessageBox::critical(this, "Error", e.what());
    }
}

void    MainWindow::init3DMenuOptions()
{
    ui->pushButtonFixedCamera->setIcon(QIcon("Images/camera_fixed.png"));
    QObject::connect(ui->pushButtonFixedCamera, SIGNAL(clicked()), this, SLOT(buttonActionFixedCamera()));

    ui->pushButtonTrackCamera->setIcon(QIcon("Images/camera_track.png"));
    QObject::connect(ui->pushButtonTrackCamera, SIGNAL(clicked()), this, SLOT(buttonActionTrackCamera()));

    ui->pushButtonFPSCamera->setIcon(QIcon("Images/camera_fps.png"));
    QObject::connect(ui->pushButtonFPSCamera, SIGNAL(clicked()), this, SLOT(buttonActionFPSCamera()));

    ui->pushButtonBrushLittle->setIcon(QIcon("Images/brush_little.png"));
    QObject::connect(ui->pushButtonBrushLittle, SIGNAL(clicked()), this, SLOT(buttonActionBrushLittle()));

    ui->pushButtonBrushMiddle->setIcon(QIcon("Images/brush_middle.png"));
    QObject::connect(ui->pushButtonBrushMiddle, SIGNAL(clicked()), this, SLOT(buttonActionBrushMiddle()));

    ui->pushButtonBrushLarge->setIcon(QIcon("Images/brush_large.png"));
    QObject::connect(ui->pushButtonBrushLarge, SIGNAL(clicked()), this, SLOT(buttonActionBrushLarge()));
}

//
// Handle button actions
//
void        MainWindow::buttonActionFixedCamera()
{
    _currentCamera = CAMERA_FIXED;
    ui->pushButtonFixedCamera->setChecked(true);
    ui->pushButtonTrackCamera->setChecked(false);
    ui->pushButtonFPSCamera->setChecked(false);
    _glDisplay->setCameraType(_currentCamera);
}

void        MainWindow::buttonActionTrackCamera()
{
    _currentCamera = CAMERA_TRACK;
    ui->pushButtonTrackCamera->setChecked(true);
    ui->pushButtonFPSCamera->setChecked(false);
    ui->pushButtonFixedCamera->setChecked(false);
    _glDisplay->setCameraType(_currentCamera);
}

void        MainWindow::buttonActionFPSCamera()
{
    _currentCamera = CAMERA_FPS;
    ui->pushButtonFPSCamera->setChecked(true);
    ui->pushButtonTrackCamera->setChecked(false);
    ui->pushButtonFixedCamera->setChecked(false);
    _glDisplay->setCameraType(_currentCamera);
}

void        MainWindow::buttonActionBrushLittle()
{
    _currentBrush = BRUSH_LITTLE;
    _glDisplay->setBrushType(_currentBrush);
    ui->pushButtonBrushLittle->setChecked(true);
    ui->pushButtonBrushMiddle->setChecked(false);
    ui->pushButtonBrushLarge->setChecked(false);
}

void        MainWindow::buttonActionBrushMiddle()
{
    _currentBrush = BRUSH_MIDDLE;
    _glDisplay->setBrushType(_currentBrush);
    ui->pushButtonBrushLarge->setChecked(false);
    ui->pushButtonBrushMiddle->setChecked(true);
    ui->pushButtonBrushLittle->setChecked(false);
}

void        MainWindow::buttonActionBrushLarge()
{
    _currentBrush = BRUSH_LARGE;
    _glDisplay->setBrushType(_currentBrush);
    ui->pushButtonBrushLittle->setChecked(false);
    ui->pushButtonBrushMiddle->setChecked(false);
    ui->pushButtonBrushLarge->setChecked(true);
}


//
// Handle menus actions
//
void    MainWindow::menuActionAbout()
{
    QMessageBox::information(this, tr("About"),
                             tr("3D game map editor\nMade by Yann Viens (student ID : 11129068)"));
}

void    MainWindow::menuActionNewMap()
{
    DialogNewMap    newMapDialog;
    unsigned int    width, height;
    Terrain*        terrain;
    Scene*          scene;

    newMapDialog.exec();
    if (newMapDialog.loadMapFromFile())
    {
        terrain = TerrainFactory::createNewTerrain(newMapDialog.getMapPath().toStdString());
        scene = _glDisplay->getScene();

        if (scene != NULL)
        {
            scene->setTerrain(terrain);
        }
        else
        {
            scene = new Scene("", "", terrain);
            _glDisplay->setScene(scene);
        }
        _glDisplay->setCameraType(_currentCamera);
    }
    else if (newMapDialog.createNewMap())
    {
        width = newMapDialog.getMapWidth();
        height = newMapDialog.getMapHeight();

        terrain = TerrainFactory::createNewTerrain(width, height, 0);
        scene = _glDisplay->getScene();

        if (scene != NULL)
        {
            scene->setTerrain(terrain);
        }
        else
        {
            scene = new Scene("", "", terrain);
            _glDisplay->setScene(scene);
        }
        _glDisplay->setCameraType(_currentCamera);
    }
}

void    MainWindow::menuActionImportTexture()
{
    DialogNewTexture    dialogNewTexture(this);

    dialogNewTexture.exec();

    if (dialogNewTexture.isLoaded())
    {
        Texture2D*  texture = new Texture2D();

        texture->setName(dialogNewTexture.getName());
        texture->setPath(dialogNewTexture.getPath());
        texture->setBehaviour(dialogNewTexture.getTextBehaviour());
        texture->load();

        _glDisplay->addTexture(texture);
        ui->listWidgetTexture->addItem(new QListWidgetItem(texture->getName().data()));
    }
}

void    MainWindow::menuActionImportModel()
{
    QString	filePath = QFileDialog::getOpenFileName(this, QString("Load model..."), QString("."), QString("Obj file (*.obj)"));

    if (filePath != "")
    {
        std::string path = filePath.toStdString();
        std::string name = path.substr(path.find_last_of('/') + 1);

        ObjModel*   obj = new ObjModel();

        try
        {
            obj->load(filePath.toStdString());
            _glDisplay->addModel(obj);
            ui->listWidgetModel->addItem(new QListWidgetItem(name.data()));
        }
        catch (std::exception e)
        {
            QMessageBox::critical(this, "Error", "Model not supported (or not fully supported).");
        }
    }
}

void    MainWindow::menuActionLoadMap()
{
    QString	filePath = QFileDialog::getOpenFileName(this, QString("Load map..."), QString("."), QString("XML file (*.xml)"));

    if (filePath != "")
    {
        Scene*      scene = _manager.load(filePath.toStdString());

        // Load scene infos to GUI component
        std::list<DrawableObject*>& objects = scene->getObjects();

        if (objects.empty() == false)
        {
            std::list<DrawableObject*>::iterator    itBegin = objects.begin();
            std::list<DrawableObject*>::iterator    itEnd = objects.end();

            while (itBegin != itEnd)
            {
                ObjModel*   model = dynamic_cast<ObjModel*>(*itBegin);
                std::string name = model->getPath();
                name = name.substr(name.find_last_of('/') + 1);
                _glDisplay->addModel(new ObjModel(*model));
                ui->listWidgetModel->addItem(new QListWidgetItem(name.data()));
                ++itBegin;
            }
        }
        Terrain*    terrain = scene->getTerrain();

        if (terrain != NULL)
        {
            Texture*    texture = terrain->getTexture();

            if (texture != NULL)
            {
                _glDisplay->addTexture(texture);
                ui->listWidgetTexture->addItem(new QListWidgetItem(texture->getName().data()));
            }
        }
        _glDisplay->setScene(scene);
        _glDisplay->setCameraType(_currentCamera);
    }
}

void    MainWindow::menuActionSaveMap()
{
    Scene*  scene = _glDisplay->getScene();

    if (scene != NULL)
    {
        if (scene->getName() != "" && scene->getPath() != "")
        {
            _manager.save(scene->getPath(), *scene);
        }
        else
            menuActionSaveMapAs();
    }
    else
    {
        QMessageBox::critical(this, "Error", "There is no map to save.");
    }
}


void    MainWindow::menuActionSaveMapAs()
{
    Scene*    scene = _glDisplay->getScene();

    if (scene != NULL)
    {
        QString	path = QFileDialog::getSaveFileName(this, QString("Save map..."), QString("."), QString("XML file (*.xml)"));

        if (path != "")
        {
            scene->setName(path.toStdString());
            scene->setPath(path.toStdString());
            _manager.save(path.toStdString(), *scene);
        }
    }
    else
    {
        QMessageBox::critical(this, "Error", "There is no map to save.");
    }
}

//
// Handle rendering menu click
//
void    MainWindow::menuActionFill()
{
    if (_glDisplay != NULL)
        _glDisplay->setDrawingMode(GL_FILL);
    ui->actionFill->setChecked(true);
    ui->actionLine->setChecked(false);
    ui->actionPoint->setChecked(false);
}

void    MainWindow::menuActionLine()
{
    if (_glDisplay != NULL)
        _glDisplay->setDrawingMode(GL_LINE);
    ui->actionLine->setChecked(true);
    ui->actionFill->setChecked(false);
    ui->actionPoint->setChecked(false);
}

void    MainWindow::menuActionPoint()
{
    if (_glDisplay != NULL)
        _glDisplay->setDrawingMode(GL_POINT);
    ui->actionPoint->setChecked(true);
    ui->actionFill->setChecked(false);
    ui->actionLine->setChecked(false);
}

//
// Handle clicks on lists
//
void    MainWindow::handleTextureListClick(QListWidgetItem* item)
{
    _glDisplay->setCurrentTexture(item->text().toStdString());
}

void    MainWindow::handleModelListClick(QListWidgetItem* item)
{
    int it = 0, nbItem = ui->listWidgetModel->count();

    while (it < nbItem)
    {
        if (ui->listWidgetModel->item(it) == item)
        {
            _glDisplay->setCurrentModel(it);
            break;
        }
        it++;
    }
}
